"""
@Author: li
@Email: lijianqiao2906@live.com
@FileName: system.py
@DateTime: 2025/07/05
@Docs: 系统相关DAO层，包含UserSettings、SystemConfig、OperationLog、LoginLog等DAO
"""

from datetime import datetime
from typing import Any

from tortoise.expressions import Q

from app.dao.base import BaseDAO
from app.models.system import LoginLog, OperationLog, SystemConfig, UserSettings
from app.utils.logger import logger


class UserSettingsDAO(BaseDAO):
    """用户配置DAO"""

    def __init__(self):
        super().__init__(UserSettings)

    async def get_by_user_id(self, user_id: int) -> UserSettings | None:
        """根据用户ID获取用户配置"""
        try:
            result = await self.model.get_or_none(user_id=user_id)
            return result if isinstance(result, UserSettings) else None
        except Exception as e:
            logger.error(f"根据用户ID获取用户配置失败: {e}")
            return None

    async def create_or_update_user_settings(
        self,
        user_id: int,
        settings_data: dict[str, Any],
    ) -> UserSettings | None:
        """创建或更新用户配置"""
        try:
            # 检查是否已存在配置
            existing_settings = await self.get_by_user_id(user_id)

            if existing_settings:
                # 更新现有配置
                for field, value in settings_data.items():
                    if hasattr(existing_settings, field):
                        setattr(existing_settings, field, value)

                await existing_settings.save()
                return existing_settings
            else:
                # 创建新配置
                settings_data["user_id"] = user_id
                result = await self.create(**settings_data)
                return result if isinstance(result, UserSettings) else None
        except Exception as e:
            logger.error(f"创建或更新用户配置失败: {e}")
            return None

    async def reset_user_settings(self, user_id: int) -> bool:
        """重置用户配置为默认值"""
        try:
            default_settings = {
                "theme": "light",
                "language": "zh-CN",
                "sidebar_collapsed": False,
                "page_size": 20,
                "timezone": "Asia/Shanghai",
                "notification_email": True,
                "notification_sms": False,
                "custom_settings": None,
            }

            result = await self.create_or_update_user_settings(user_id, default_settings)
            return result is not None
        except Exception as e:
            logger.error(f"重置用户配置失败: {e}")
            return False

    async def update_theme(self, user_id: int, theme: str) -> bool:
        """更新用户主题"""
        try:
            settings = await self.get_by_user_id(user_id)
            if settings:
                settings.theme = theme
                await settings.save()
                return True
            return False
        except Exception as e:
            logger.error(f"更新用户主题失败: {e}")
            return False

    async def update_language(self, user_id: int, language: str) -> bool:
        """更新用户语言"""
        try:
            settings = await self.get_by_user_id(user_id)
            if settings:
                settings.language = language
                await settings.save()
                return True
            return False
        except Exception as e:
            logger.error(f"更新用户语言失败: {e}")
            return False


class SystemConfigDAO(BaseDAO):
    """系统配置DAO"""

    def __init__(self):
        super().__init__(SystemConfig)

    async def get_by_key(self, key: str) -> SystemConfig | None:
        """根据配置键获取配置"""
        try:
            result = await self.model.get_or_none(key=key)
            return result if isinstance(result, SystemConfig) else None
        except Exception as e:
            logger.error(f"根据配置键获取配置失败: {e}")
            return None

    async def get_by_category(self, category: str) -> list[SystemConfig]:
        """根据分类获取配置列表"""
        try:
            results = await self.model.filter(category=category).all()
            return [result for result in results if isinstance(result, SystemConfig)]
        except Exception as e:
            logger.error(f"根据分类获取配置列表失败: {e}")
            return []

    async def get_public_configs(self) -> list[SystemConfig]:
        """获取所有公开配置"""
        try:
            results = await self.model.filter(is_public=True).all()
            return [result for result in results if isinstance(result, SystemConfig)]
        except Exception as e:
            logger.error(f"获取公开配置失败: {e}")
            return []

    async def get_configs_by_keys(self, keys: list[str]) -> list[SystemConfig]:
        """根据配置键列表批量获取配置"""
        try:
            results = await self.model.filter(key__in=keys).all()
            return [result for result in results if isinstance(result, SystemConfig)]
        except Exception as e:
            logger.error(f"批量获取配置失败: {e}")
            return []

    async def update_config_value(self, key: str, value: str) -> bool:
        """更新配置值"""
        try:
            config = await self.get_by_key(key)
            if config and not config.is_readonly:
                config.value = value
                await config.save()
                return True
            return False
        except Exception as e:
            logger.error(f"更新配置值失败: {e}")
            return False

    async def batch_update_configs(self, configs: dict[str, str]) -> bool:
        """批量更新配置"""
        try:
            existing_configs = await self.get_configs_by_keys(list(configs.keys()))

            for config in existing_configs:
                if not config.is_readonly and config.key in configs:
                    config.value = configs[config.key]
                    await config.save()

            return True
        except Exception as e:
            logger.error(f"批量更新配置失败: {e}")
            return False

    async def is_config_exists(self, key: str) -> bool:
        """检查配置是否存在"""
        try:
            return await self.model.filter(key=key).exists()
        except Exception as e:
            logger.error(f"检查配置是否存在失败: {e}")
            return False

    async def create_config(
        self,
        key: str,
        value: str,
        category: str,
        type: str = "string",
        is_public: bool = False,
        is_readonly: bool = False,
        description: str | None = None,
    ) -> SystemConfig | None:
        """创建新配置"""
        try:
            if await self.is_config_exists(key):
                logger.warning(f"配置键 {key} 已存在")
                return None

            result = await self.create(
                key=key,
                value=value,
                category=category,
                type=type,
                is_public=is_public,
                is_readonly=is_readonly,
                description=description,
            )
            return result if isinstance(result, SystemConfig) else None
        except Exception as e:
            logger.error(f"创建配置失败: {e}")
            return None


class OperationLogDAO(BaseDAO):
    """操作日志DAO"""

    def __init__(self):
        super().__init__(OperationLog)

    async def create_log(
        self,
        user_id: int,
        module: str,
        action: str,
        method: str,
        path: str,
        ip_address: str,
        response_code: int,
        response_time: int,
        resource_id: str | None = None,
        resource_type: str | None = None,
        user_agent: str | None = None,
        request_data: dict | None = None,
        description: str | None = None,
    ) -> OperationLog | None:
        """创建操作日志"""
        try:
            result = await self.create(
                user_id=user_id,
                module=module,
                action=action,
                resource_id=resource_id,
                resource_type=resource_type,
                method=method,
                path=path,
                ip_address=ip_address,
                user_agent=user_agent,
                request_data=request_data,
                response_code=response_code,
                response_time=response_time,
                description=description,
            )
            return result if isinstance(result, OperationLog) else None
        except Exception as e:
            logger.error(f"创建操作日志失败: {e}")
            return None

    async def get_user_logs(
        self,
        user_id: int,
        page: int = 1,
        page_size: int = 20,
        start_date: datetime | None = None,
        end_date: datetime | None = None,
    ) -> dict[str, Any]:
        """获取用户操作日志"""
        try:
            queryset = self.model.filter(user_id=user_id)

            # 时间范围过滤
            if start_date:
                queryset = queryset.filter(created_at__gte=start_date)
            if end_date:
                queryset = queryset.filter(created_at__lte=end_date)

            return await self.paginate(queryset, page, page_size)
        except Exception as e:
            logger.error(f"获取用户操作日志失败: {e}")
            return {"items": [], "total": 0, "page": page, "page_size": page_size}

    async def get_logs_by_module(
        self,
        module: str,
        page: int = 1,
        page_size: int = 20,
    ) -> dict[str, Any]:
        """根据模块获取操作日志"""
        try:
            queryset = self.model.filter(module=module)
            return await self.paginate(queryset, page, page_size)
        except Exception as e:
            logger.error(f"根据模块获取操作日志失败: {e}")
            return {"items": [], "total": 0, "page": page, "page_size": page_size}

    async def get_logs_by_action(
        self,
        action: str,
        page: int = 1,
        page_size: int = 20,
    ) -> dict[str, Any]:
        """根据操作类型获取日志"""
        try:
            queryset = self.model.filter(action=action)
            return await self.paginate(queryset, page, page_size)
        except Exception as e:
            logger.error(f"根据操作类型获取日志失败: {e}")
            return {"items": [], "total": 0, "page": page, "page_size": page_size}

    async def search_logs(
        self,
        keyword: str,
        page: int = 1,
        page_size: int = 20,
        user_id: int | None = None,
        module: str | None = None,
        start_date: datetime | None = None,
        end_date: datetime | None = None,
    ) -> dict[str, Any]:
        """搜索操作日志"""
        try:
            # 构建查询条件
            query = Q()

            # 关键字搜索
            if keyword:
                query &= (
                    Q(action__icontains=keyword)
                    | Q(module__icontains=keyword)
                    | Q(path__icontains=keyword)
                    | Q(description__icontains=keyword)
                )

            # 用户过滤
            if user_id:
                query &= Q(user_id=user_id)

            # 模块过滤
            if module:
                query &= Q(module=module)

            # 时间范围过滤
            if start_date:
                query &= Q(created_at__gte=start_date)
            if end_date:
                query &= Q(created_at__lte=end_date)

            queryset = self.model.filter(query)
            return await self.paginate(queryset, page, page_size)
        except Exception as e:
            logger.error(f"搜索操作日志失败: {e}")
            return {"items": [], "total": 0, "page": page, "page_size": page_size}

    async def get_statistics(
        self,
        start_date: datetime | None = None,
        end_date: datetime | None = None,
    ) -> dict[str, Any]:
        """获取操作日志统计"""
        try:
            queryset = self.model.all()

            # 时间范围过滤
            if start_date:
                queryset = queryset.filter(created_at__gte=start_date)
            if end_date:
                queryset = queryset.filter(created_at__lte=end_date)

            # 总数统计
            total_count = await queryset.count()

            # 成功/失败统计
            success_count = await queryset.filter(response_code__lt=400).count()
            error_count = await queryset.filter(response_code__gte=400).count()

            return {
                "total_count": total_count,
                "success_count": success_count,
                "error_count": error_count,
                "success_rate": round(success_count / total_count * 100, 2) if total_count > 0 else 0,
            }
        except Exception as e:
            logger.error(f"获取操作日志统计失败: {e}")
            return {"total_count": 0, "success_count": 0, "error_count": 0, "success_rate": 0}


class LoginLogDAO(BaseDAO):
    """登录日志DAO"""

    def __init__(self):
        super().__init__(LoginLog)

    async def create_login_log(
        self,
        user_id: int,
        login_type: str,
        ip_address: str,
        is_success: bool,
        login_at: datetime,
        location: str | None = None,
        device: str | None = None,
        user_agent: str | None = None,
        failure_reason: str | None = None,
        description: str | None = None,
    ) -> LoginLog | None:
        """创建登录日志"""
        try:
            result = await self.create(
                user_id=user_id,
                login_type=login_type,
                ip_address=ip_address,
                location=location,
                device=device,
                user_agent=user_agent,
                is_success=is_success,
                failure_reason=failure_reason,
                login_at=login_at,
                description=description,
            )
            return result if isinstance(result, LoginLog) else None
        except Exception as e:
            logger.error(f"创建登录日志失败: {e}")
            return None

    async def update_logout_time(self, log_id: int, logout_at: datetime) -> bool:
        """更新登出时间"""
        try:
            log = await self.get_by_id(log_id)
            if log:
                log.logout_at = logout_at
                await log.save()
                return True
            return False
        except Exception as e:
            logger.error(f"更新登出时间失败: {e}")
            return False

    async def get_user_login_logs(
        self,
        user_id: int,
        page: int = 1,
        page_size: int = 20,
        start_date: datetime | None = None,
        end_date: datetime | None = None,
    ) -> dict[str, Any]:
        """获取用户登录日志"""
        try:
            queryset = self.model.filter(user_id=user_id)

            # 时间范围过滤
            if start_date:
                queryset = queryset.filter(login_at__gte=start_date)
            if end_date:
                queryset = queryset.filter(login_at__lte=end_date)

            return await self.paginate(queryset, page, page_size)
        except Exception as e:
            logger.error(f"获取用户登录日志失败: {e}")
            return {"items": [], "total": 0, "page": page, "page_size": page_size}

    async def get_failed_login_logs(
        self,
        page: int = 1,
        page_size: int = 20,
        start_date: datetime | None = None,
        end_date: datetime | None = None,
    ) -> dict[str, Any]:
        """获取登录失败日志"""
        try:
            queryset = self.model.filter(is_success=False)

            # 时间范围过滤
            if start_date:
                queryset = queryset.filter(login_at__gte=start_date)
            if end_date:
                queryset = queryset.filter(login_at__lte=end_date)

            return await self.paginate(queryset, page, page_size)
        except Exception as e:
            logger.error(f"获取登录失败日志失败: {e}")
            return {"items": [], "total": 0, "page": page, "page_size": page_size}

    async def get_recent_login_by_user(
        self,
        user_id: int,
        limit: int = 10,
    ) -> list[LoginLog]:
        """获取用户最近登录记录"""
        try:
            results = await self.model.filter(user_id=user_id, is_success=True).order_by("-login_at").limit(limit).all()
            return [result for result in results if isinstance(result, LoginLog)]
        except Exception as e:
            logger.error(f"获取用户最近登录记录失败: {e}")
            return []

    async def count_failed_attempts(
        self,
        user_id: int,
        since: datetime,
    ) -> int:
        """统计指定时间后的登录失败次数"""
        try:
            return await self.model.filter(
                user_id=user_id,
                is_success=False,
                login_at__gte=since,
            ).count()
        except Exception as e:
            logger.error(f"统计登录失败次数失败: {e}")
            return 0

    async def get_login_statistics(
        self,
        start_date: datetime | None = None,
        end_date: datetime | None = None,
    ) -> dict[str, Any]:
        """获取登录统计信息"""
        try:
            queryset = self.model.all()

            # 时间范围过滤
            if start_date:
                queryset = queryset.filter(login_at__gte=start_date)
            if end_date:
                queryset = queryset.filter(login_at__lte=end_date)

            # 总登录次数
            total_logins = await queryset.count()

            # 成功登录次数
            success_logins = await queryset.filter(is_success=True).count()

            # 失败登录次数
            failed_logins = await queryset.filter(is_success=False).count()

            # 独立用户数
            unique_users = len(await queryset.filter(is_success=True).distinct().values_list("user_id", flat=True))

            return {
                "total_logins": total_logins,
                "success_logins": success_logins,
                "failed_logins": failed_logins,
                "unique_users": unique_users,
                "success_rate": round(success_logins / total_logins * 100, 2) if total_logins > 0 else 0,
            }
        except Exception as e:
            logger.error(f"获取登录统计信息失败: {e}")
            return {
                "total_logins": 0,
                "success_logins": 0,
                "failed_logins": 0,
                "unique_users": 0,
                "success_rate": 0,
            }

    async def get_login_trends(
        self,
        days: int = 7,
    ) -> list[dict[str, Any]]:
        """获取登录趋势数据"""
        try:
            from datetime import timedelta

            end_date = datetime.now()
            start_date = end_date - timedelta(days=days)

            # 这里简化实现，实际可能需要按日期分组统计
            logs = await self.model.filter(
                login_at__gte=start_date,
                login_at__lte=end_date,
            ).all()

            # 按日期分组统计（简化版本）
            daily_stats = {}
            for log in logs:
                if isinstance(log, LoginLog):
                    date_key = log.login_at.date().isoformat()
                    if date_key not in daily_stats:
                        daily_stats[date_key] = {"date": date_key, "success": 0, "failed": 0}

                    if log.is_success:
                        daily_stats[date_key]["success"] += 1
                    else:
                        daily_stats[date_key]["failed"] += 1

            return list(daily_stats.values())
        except Exception as e:
            logger.error(f"获取登录趋势数据失败: {e}")
            return []


# DAO实例
user_settings_dao = UserSettingsDAO()
system_config_dao = SystemConfigDAO()
operation_log_dao = OperationLogDAO()
login_log_dao = LoginLogDAO()
